﻿using System;
using System.Data;

namespace BizElements.Core
{
    /// <summary>Transforms single record to and from object array and ADO.NET DataRow object using reflection.</summary>
    public class RecordTransformer : IRecordTransformer
    {
        /// <summary>Gets or sets the entity that is transformed by transformer.</summary>
        public virtual IEntity TransformedEntity { get; set; }

        /// <summary>Initializes entity members with data stored in the given DataRow.</summary>
        /// <param name="row">DataRow with the required columns.</param>
        public virtual void FromDataRow(DataRow row)
        {
            FromDataRow(row, this.TransformedEntity.Table);
        }

        /// <summary>Initializes entity members with data stored in the given DataRow.</summary>
        /// <param name="row">DataRow with all or some of the columns defined in meta data.</param>
        /// <param name="fieldMetadata"><see cref="IDbTable"/> meta data object which links ADO.NET row columns to entity properties.</param>
        /// <remarks>This method, when used in combination with <see cref="IDbTable"/> objects that contain columns with
        /// prefixed aliases, enables you to initialize multiple entities that belong to different tables with data from a single DataRow.</remarks>
        public virtual void FromDataRow(DataRow row, IDbTable fieldMetadata)
        {
            foreach (IDbColumn field in fieldMetadata.Columns)
            {
                object fieldValue = GetColumnValue(row, field);
                ReflectionUtil.SetPropertyValue(this.TransformedEntity, field.PropertyName, fieldValue);
            }
        }

        private static object GetColumnValue(DataRow row, IDbColumn column)
        {
            int colIdx = row.Table.Columns.IndexOf(column.Alias);
            if (colIdx < 0)
                colIdx = row.Table.Columns.IndexOf(column.ColumnName);
            if (colIdx < 0)
                throw new ArgumentException(Messages.EntityModelBase_TableDoesntContainSpecifiedColumn + column.ColumnName, "column");

            object val = row[colIdx];
            if (val == DBNull.Value)
                return null;
            else
                return val;
        }

        /// <summary>Creates an array of objects containing entity data suitable for copying and importing into <see cref="DataTable"/> </summary>	
        /// <returns>Entity values.</returns>	
        public object[] ToObjectArray()
        {
            var entity = this.TransformedEntity;
            var columns = entity.Table.Columns;
            object[] values = new object[columns.Count];
            for (int i = 0; i < columns.Count; i++)
                values[i] = ReflectionUtil.GetPublicPropertyValue(entity, columns[i].PropertyName);

            return values;
        }

        /// <summary>Initializes entity members with the given values.</summary>
        /// <param name="entityValues">Array with the required values.</param>	
        public void FromObjectArray(object[] entityValues)
        {
            var entity = this.TransformedEntity;
            var columns = entity.Table.Columns;
            if (columns.Count != entityValues.Length)
                throw new ArgumentException("Provided array length does not equal the number of columns in table to which the entity is mapped.", "entityValues");

            for (int i = 0; i < columns.Count; i++)
                ReflectionUtil.SetPropertyValue(entity, columns[i].PropertyName, entityValues[i]);
        }
    }
}
